#include "StdAfx.h"



#include "viafirmaClient.h"
#include "netUtil.h"
#include "includes\json\json.h"
#include <capi.h>


#include <fstream>

#include <iostream>
#include "Base64.h"
#include "base_64.h"
#include "lib_sha1\sha1.h"
#include <sstream>
#include <string>
#include <iomanip>
#include <algorithm>
#include <cctype>



SignedDocument::~SignedDocument(){
	delete [] data;
	delete [] X509;
}



bool to_bool(std::string str) {
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);
    std::istringstream is(str);
    bool b;
    is >> std::boolalpha >> b;
    return b;
}


viafirmaClient::viafirmaClient(std::string urlViafirma)
{
	url=urlViafirma+"/rest/services/ViafirmaAPI3/";
	std::cout<<"Init Viafirma Curl ."<<VIAFIRMA_CLIENT_VERSION<<" URL REST: "<<urlViafirma;

}




viafirmaClient::~viafirmaClient(void)
{

}

wstring string2wstring(string str) {
	wstring wstr(str.length(),L'');
	copy(str.begin(),str.end(),wstr.begin());
	return wstr;
}

SignedDocument * viafirmaClient::getSignedDocumentData(std::string idSign) {
	netUtil networkUtil;
	SignedDocument * info= new SignedDocument;
	bool isInit=networkUtil.isCurlInit();

	if(!isInit){
		 printf( "\nConfiguration requited!");
	}

	std::string urlMethod=url+"getSignedDocumentData";

	std::string post_data="param1="+idSign;
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());

	std::string result;
	if( ok )
    {
        networkUtil.getLastWebResponse( result );
      
		// process base 64
		const char * dataBase64=result.c_str();
		std::string temp(dataBase64);
		std::string resultDecodeBase64=base64_decode(temp);
		const char* dataStr=resultDecodeBase64.c_str();
		int realLen=resultDecodeBase64.length();

		// dynamic memory	
		char * data=new char [realLen];
		//strcpy(data,(char *)dataStr);
		int i;
		for(i=0; i<realLen; i++){
	     data[i]=dataStr[i];
		}
		info->id=idSign;
		info->data=data;
		info->dataLen=realLen;
	
    }
    else
    {
        networkUtil.getLastCurlError( result );
        printf( "\ncURL error response:\n %s\n", result.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		throw "Unable to sign. "+result+ "-"+msg;
    }



	return info;
}



SignedDocument * viafirmaClient::getSignedDocument(std::string idSign) {
	netUtil networkUtil;
	SignedDocument * info= new SignedDocument;
	bool isInit=networkUtil.isCurlInit();

	if(!isInit){
		 printf( "\nConfiguration requited!");
	}

	std::string urlMethod=url+"getSignedDocument";

	std::string post_data="param1="+idSign;
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());

	std::string result;
	if( ok )
    {
		networkUtil.getLastWebResponse( result );
        printf( "\n response:\n %s\n", result.c_str() );
		
		Json::Value root;   // will contains the root value after parsing.
		Json::Reader reader;
		bool parsingSuccessful = reader.parse( result, root );
		if ( !parsingSuccessful )
		{
			// report to the user the failure and their locations in the document.
			std::cout  << "Failed to parse configuration\n"
					   << reader.getFormatedErrorMessages();

			throw "invalid response. "+reader.getFormatedErrorMessages();
		}else{
			const Json::Value signInfo = root["SignedDocument"];
			std::string base64 = signInfo.get("data","0" )[(Json::Value::UInt)0].asString();
			
			  
			// process base 64
			const char * dataBase64=base64.c_str();
			std::string temp(dataBase64);
			std::string resultDecodeBase64=base64_decode(temp);
			const char* dataStr=resultDecodeBase64.c_str();
			int realLen=resultDecodeBase64.length();

			// dynamic memory	TODO
			char * data=new char [realLen];
			//strcpy(data,(char *)dataStr);
			int i;
			for(i=0; i<realLen; i++){
			 data[i]=dataStr[i];
			}

			Json::Value metadata=signInfo.get("metadata","0" );
			for(int i=0;i<metadata.size();i++){
				Json::Value  metadataItem=metadata[(Json::Value::UInt)i];
			
				Json::Value value1=metadataItem[(Json::Value::UInt)0];//[(Json::Value::UInt)0];
				Json::Value value2=metadataItem[(Json::Value::UInt)1];//[(Json::Value::UInt)1];

				std::string type=value1.asString();
				std::string value=value2.asString();


				if("rsaKeySize" == type){
					info->rsaKeySize=value;
				}else if("X509" == type){
					// process base 64
					const char * x509Base64=value.c_str();
					std::string tempX509(x509Base64);
					std::string result509Base64=base64_decode(tempX509);
					const char* dataStrX509=result509Base64.c_str();
					int realLenX509=result509Base64.length();

					// dynamic memory TODO: improve
					char * dataX509=new char [realLenX509];
					int i;
					for(i=0; i<realLenX509; i++){
						dataX509[i]=dataStrX509[i];
					}
					info->X509=dataX509;
					info->X509Len=realLenX509;
				}else if("serialNumber" == type){
					info->serialNumber=value;
				}else if("issuerDN" == type){
					info->issuerDN=value;
				}
				

					

					

			}


			info->id=idSign;
			info->data=data;
			info->dataLen=realLen;
	

			return info;
		}


	
    
    }
    else
    {
        networkUtil.getLastCurlError( result );
        printf( "\ncURL error response:\n %s\n", result.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		throw "Unable to sign. "+result+ "-"+msg;
    }
	return info;
}


std::string viafirmaClient::signByServer(std::string nameDocument, unsigned char * dataToSign,int dataSize, std::string alias, std::string password,std::string tileFormat,std::string formatSign){
	netUtil networkUtil;

	bool isInit=networkUtil.isCurlInit();

	if(isInit){
		 printf( "\nConfiguration: :%s\n", url.c_str() );
	}

	std::string urlMethod=url+"signByServer";

	std::string dataBase64 = base64_encode(dataToSign,dataSize);
  //std::string decoded = base64_decode(encoded);
  //std::cout << "encoded: " << encoded << std::endl;
  //std::cout << "decoded: " << decoded << std::endl;


	char * data=networkUtil.encode(dataBase64.c_str());
	std::string post_data="param1="+nameDocument+"&param2="+data+"&param3="+alias+"&param4="+password+"&param5="+tileFormat+"&param6="+formatSign;
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());

	std::string retult;
	 if( ok )
    {
        networkUtil.getLastWebResponse( retult );
        printf( "\n response:\n %s\n", retult.c_str() );
    }
    else
    {
        networkUtil.getLastCurlError( retult );
        printf( "\ncURL error response:\n %s\n", retult.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		throw "Unable to sign. "+retult+ "-"+msg;
		
    }
	 return retult;
}

/**
* Sign document.
* Compute sha1 and send to server only sha1
*/
std::string viafirmaClient::signByServerWithSha1(std::string nameDocument, unsigned char * dataToSign,int dataSize, std::string alias, std::string password,std::string formatSign){
	netUtil networkUtil;

	bool isInit=networkUtil.isCurlInit();

	if(isInit){
		 printf( "\nConfiguration: :%s\n", url.c_str() );
	}

	std::string urlMethod=url+"signByServerWithRawSha1";

	
	// compute SHA1
	 SHA1        sha;                        // SHA-1 class
	 sha.Reset();
	 sha.Input(dataToSign,dataSize);
	 unsigned    message_digest[5];
	 sha.Result(message_digest);
	 char * rawSha1=new char[5*CHAR_BIT+(4)]; // 5 block *4 bits per block + 4 blank
	 sprintf(rawSha1,"%08X %08X %08X %08X %08X",message_digest[0],
                    message_digest[1],
                    message_digest[2],
                    message_digest[3],
                    message_digest[4]);
	 char * data=networkUtil.encode(rawSha1);
	
	
	std::string post_data="param1="+nameDocument+"&param2="+data+"&param3="+alias+"&param4="+password+"&param5="+TYPE_FILE_HASH_BINARRY+"&param6="+formatSign;
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());
	
	std::string retult;
	 if( ok )
    {
        networkUtil.getLastWebResponse( retult );
        printf( "\n response:\n %s\n", retult.c_str() );
    }
    else
    {
        networkUtil.getLastCurlError( retult );
        printf( "\ncURL error response:\n %s\n", retult.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		throw "Unable to sign. "+retult+ "-"+msg;
		
    }
	 return retult;
}


/**
* Validaten document.
* Compute sha1 and send to server only sha1
*/
bool viafirmaClient::validateWithSha1(unsigned char * dataSigned,int dataSize,unsigned char * signInfo,int signSize,std::string formatSign){
	netUtil networkUtil;
	bool isInit=networkUtil.isCurlInit();

	if(isInit){
		 printf( "\nConfiguration: :%s\n", url.c_str() );
	}

	std::string urlMethod=url+"validateSha1";

	
	// compute SHA1
	 SHA1        sha;                        // SHA-1 class
	 sha.Reset();
	 sha.Input(dataSigned,dataSize);
	 unsigned    message_digest[5];
	 sha.Result(message_digest);
	  char * rawSha1=new char[5*CHAR_BIT+(4)]; // 5 block *4 bits per block + 4 blank
	 sprintf(rawSha1,"%08X %08X %08X %08X %08X",message_digest[0],
                    message_digest[1],
                    message_digest[2],
                    message_digest[3],
                    message_digest[4]);
	char * data=networkUtil.encode(rawSha1);

	 // encode sign
	 std::string signInfoBase64 = base64_encode(signInfo,signSize);
 	char * dataSignInfoBase64=networkUtil.encode(signInfoBase64.c_str());

	std::string temp="";
	std::string post_data="temp="+temp+"&param1="+data+"&param2="+dataSignInfoBase64+"&param3="+formatSign;
	
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());



	std::string result;
	 if( ok )
    {
        networkUtil.getLastWebResponse( result );
        printf( "\n response:\n %s\n", result.c_str() );
		return to_bool(result);
    }
    else
    {
        networkUtil.getLastCurlError( result );
        printf( "\ncURL error response:\n %s\n", result.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		return false;
		
    }
}


std::string viafirmaClient::ping(std::string msg){
	netUtil networkUtil;

	bool isInit=networkUtil.isCurlInit();

	if(isInit){
		 printf( "\nConfiguration: :%s\n", url.c_str() );
	}

	std::string urlMethod=url+"ping";
	std::string post_data="param1="+msg;
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());

	std::string retult;
	 if( ok )
    {
        networkUtil.getLastWebResponse( retult );
        printf( "\n response:\n %s\n", retult.c_str() );
    }
    else
    {
        networkUtil.getLastCurlError( retult );
        printf( "\ncURL error response:\n %s\n", retult.c_str() );
		throw "Unable to connect to  "+urlMethod+". "+retult;
    }
	 return retult;
}



/**
* Validaten document.
* send orignal data to server
*/
bool viafirmaClient::checkOrignalVSSignedDocument(unsigned char * dataSigned,int dataSize,unsigned char * signInfo,int signSize,std::string formatSign){
	netUtil networkUtil;
	bool isInit=networkUtil.isCurlInit();

	if(isInit){
		 printf( "\nConfiguration: :%s\n", url.c_str() );
	}

	std::string urlMethod=url+"checkOrignalVSSignedDocument";

	

	 std::string signedDataBase64 = base64_encode(dataSigned,dataSize);
	 char * data=networkUtil.encode(signedDataBase64.c_str());

	 // encode sign
	 std::string signInfoBase64 = base64_encode(signInfo,signSize);
 	char * dataSignInfoBase64=networkUtil.encode(signInfoBase64.c_str());

	std::string temp="";
	std::string post_data="temp="+temp+"&param1="+data+"&param2="+dataSignInfoBase64+"&param3="+formatSign;
	
	bool ok=networkUtil.performPost(urlMethod,post_data.c_str());

	std::string result;
	 if( ok )
    {
        networkUtil.getLastWebResponse( result );
        printf( "\n response:\n %s\n", result.c_str() );
		
		Json::Value root;   // will contains the root value after parsing.
		Json::Reader reader;
		bool parsingSuccessful = reader.parse( result, root );
		if ( !parsingSuccessful )
		{
			// report to the user the failure and their locations in the document.
			std::cout  << "Failed to parse configuration\n"
					   << reader.getFormatedErrorMessages();

			throw "invalid response. "+reader.getFormatedErrorMessages();
		}else{
			const Json::Value signatureInfo = root["SignatureInfoViafirma"];
			bool valid = signatureInfo.get("valid", "false" ).asBool();
			std::string message = signatureInfo.get("message", "" ).asString();


			return valid;
		}



    }
    else
    {
        networkUtil.getLastCurlError( result );
        printf( "\ncURL error response:\n %s\n", result.c_str() );
		std::string msg;
		networkUtil.getLastWebResponse( msg );
        printf( "\nc response:\n %s\n", msg.c_str() );
		return false;
		
    }
}